#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Client.h>
#include <Kharlia/Engine/Init.h>
#include <Kharlia/Engine/RenderView.h>
#include <Kharlia/Engine/Engine.h>
#include <Kharlia/Engine/Player.h>
#include <Kharlia/Engine/Actor.h>
#include <Kharlia/Core/IO.h>

#include <CEGUIWindowFactory.h>
#include <CEGUIImagesetManager.h>
#include <CEGUISchemeManager.h>
#include <CEGUIWindowManager.h>
#include <CEGUIFontManager.h>
#include <CEGUIWindowFactoryManager.h>
#include <CEGUITplWindowFactory.h>
#include <CEGUIScriptModule.h>
#include <falagard/CEGUIFalWidgetLookManager.h>

#include <RendererModules/OpenGL/CEGUIOpenGLRenderer.h>

#define ENABLE_FRAME_COUNTER 1
#define ENABLE_TEST_DIALOG 0
#define ENABLE_MAIN_RENDERVIEW 0
#define ENABLE_SCALING 0
#define ENABLE_CONSOLE 0

namespace kharlia { namespace engine {

using ui::URect;
using ui::UVector2;
using ui::UDim;

//-----------
// FResourceProvider
//-----------

void FResourceProvider::loadRawDataContainer(const ui::String& filename,
                                             ui::RawDataContainer& output,
                                             const ui::String& resourceGroup) {
    if (core::IsCurrentTaskletMain()) {
        // Default blocking IO if we're on main
        format fmt("Resource provider is loading from the main tasklet: %1%, %2%");
        fmt % filename.c_str() % resourceGroup.c_str();
        mLogger->Warning(fmt);
        ui::DefaultResourceProvider::loadRawDataContainer(filename, output, resourceGroup);
    }
    else {
        // Adapted DefaultResourceProvider code to stackless
        if (filename.empty()) {
            throw ui::InvalidRequestException(
                "Kharlia::FResourceProvider::load - Filename supplied for data loading must be valid");
        }
        ui::String finalFilename(getFinalFilename(filename, resourceGroup));
        // Create and execute the request
        intrusive_ptr<core::FFileLoadRequest> req(
            new core::FFileLoadRequest(std::string(finalFilename.c_str()), 0, 0)
        );
        req->ExecuteStackless();
        // Handle an error
        switch (req->GetErrorCode()) {
        case core::IOERR_NONE:
            break;
        case core::IOERR_OPENFAIL:
            throw ui::InvalidRequestException(
                "Kharlia::FResourceProvider::load - " + filename + " does not exist");
        default:
            throw ui::GenericException(
                "Kharlia::FResourceProvider::loadRawDataContainer - Problem reading " + filename);
        }
        // Copy our result into a buffer that CEGUI can use; must be deleted manually
        const std::string& data = req->GetResult();
        uint8* buffer = new uint8[data.size()];
        memcpy(buffer, data.c_str(), data.size());
        output.setData(buffer);
        output.setSize(data.size());
    }
}
void FResourceProvider::unloadRawDataContainer(ui::RawDataContainer& data) {
    uint8* ptr = data.getDataPtr();
    delete[] ptr;
    data.setData(NULL);
    data.setSize(0);
}

//-----------
// CClient
//-----------

CClient::CClient(PyObject* self):
    CObject(self),
    mLogger(),
    mInited(false),
    mMousePos(0, 0),
    mCamera(),
    mWindowHandle(0),
    mViewportSize(),
    mIsLeftArrowPressed(false),
    mIsRightArrowPressed(false),
    mIsUpArrowPressed(false),
    mIsDownArrowPressed(false),

    // Client GUI
    mUiInited(false),
    mUiFocused(false),
    mUiRenderer(NULL),
    mUiSystem(NULL),
    mUiResourceProvider(NULL),
    mUiWindowManager(NULL),
    mUiSchemeManager(NULL),
    mUiFontManager(NULL),
    mUiRenderViews(),
    mUiDebugText(NULL),

    // Renderer
    mRnInited(false),
    mRnRendering(false),
    mRnShowActorBounds(false),
    mRnDeviceContext(0),
    mRnRenderingContext(0),
    mRnErrorTexture(NULL),
    mRnTilesetTexture(NULL),
    mRnVertexBuffers(100),

    mEditorRenderInfo(NULL)
{
    C_ASSERT(sizeof(mKeyPressTable[0]) == 1);
    for (int i = 0; i < sizeof(mKeyPressTable); i++) {
        mKeyPressTable[i] = false;
    }
}

CClient::~CClient() {
    assert(!mInited);
}

void CClient::Init(shared_ptr<CEngine> eng, object window) {
    if (mInited)
        return;
    mEngine = eng;
    assert(eng);
    mLogger = core::FLogger::GetLogger("client");
    mLogger->Info("initializing client");

    // Get the window handle
    mWindowHandle = extract<uint32>(window.attr("GetWindowHandle")());
    // Get the viewport size
    mViewportSize = extract<FSizeI>(window.attr("GetSize")());
  
    if (mViewportSize.Width == 0 || mViewportSize.Height == 0)
        throw RuntimeError("Client initialization failed; viewport size not set");
    if (!mWindowHandle || !::IsWindow(reinterpret_cast<HWND>(mWindowHandle)))
        throw RuntimeError("Client initialization failed; invalid window handle");

    RnInit();
    UiInit();

    // Load the testing tileset
    const char* ttfn = extract<const char*>(mEngine->GetConfig()["TestTilesetFileName"]);
    mRnTilesetTexture = &mUiRenderer->createTexture(ttfn, "tilesets");

    mViewport = window;
    mInited = true;
    mViewport.attr("OnClientInited")(GetSelf());
}

void CClient::Exit() {
    if (!mInited)
        return;

    mViewport.attr("OnClientExiting")();

    UiFini();
    RnFini();

    mLogger.reset();
    mEngine.reset();
    mViewport = object();
    mInited = false;
}

void CClient::Tick(float deltaTime) {
    mRnRendering = true;
    // Render the scene
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    static double renderStart = 0, renderTotal = 0;
    static int renderCount = 0;
    {
        shared_ptr<ACamera> cam;
        bool isClientCamera = false;
        //assert(mLocalPlayer);
        //if (mLocalPlayer->GetController() &&
        //    mLocalPlayer->GetController()->GetCamera() &&
        //    mLocalPlayer->GetController()->GetCamera()->GetLevel()) {
        //    cam = mLocalPlayer->GetController()->GetCamera();
        //}
        if (mCamera) {
            if (!mCamera->GetOwner() || !mCamera->GetOwner()->GetLevel() ||
                    mCamera->GetLevel()->GetWorld() != mEngine->GetWorld())
                mCamera.reset();
            else
                cam = mCamera;
        }
        if (cam) {
            renderStart = core::StatGetClock();
            RnDrawScene(cam, FRectF(0, 0, float(mViewportSize.Width), float(mViewportSize.Height)));
            renderTotal += core::StatGetClock() - renderStart;
            renderCount++;
        }
    }
    // CEGUI work and rendering
    if (mUiInited) {
        mUiSystem->injectTimePulse(float(deltaTime));
#if ENABLE_FRAME_COUNTER
        // FPS Counter
        static double elapsed = 0;
        static uint32 frameCount = 0, totalFrameCount = 0;

        elapsed += deltaTime;
        frameCount++;
        totalFrameCount++;

        if (elapsed >= 1.0) {
            // Get proper camera position
            FVector3F pos;
            if (mCamera && mCamera->GetTarget()) {
                pos = mCamera->GetTarget()->GetPosition();
                pos += mCamera->GetTargetOffset();
            }

            // Calc average scene rendering time
            double renderAvg = renderTotal / renderCount;
            renderTotal = 0;
            renderCount = 0;

            // Format the text to be sent to the window
            static boost::format txt("%1% FPS, RT: %6%, GLErr: %2%, FC: %3%, CamPos: (%4%, %5%)");
            txt % frameCount %
                  glGetError() %
                  totalFrameCount %
                  pos.X %
                  pos.Y %
                  (renderAvg * 1000);
            
            mUiDebugText->setText(txt.str());
            elapsed = 0;
            frameCount = 0;
        }
#endif
        // RenderViews are redrawn every frame
        for (uint i = 0; i < mUiRenderViews.size(); i++) {
            FRenderView* rv = mUiRenderViews[i];
            if (rv->GetDrawEveryFrame()) {
                rv->invalidate();
            }
        }
        mUiSystem->renderGUI();
    }
    // Done rendering
    SwapBuffers(mRnDeviceContext);
    mRnRendering = false;
}

FVector2F CClient::ConvertScreenToTileCoords(const FVector2I& target) {
    if (!mCamera)
        return FVector2F();
    FVector2F out;

    float vcl = mCamera->GetTarget()->GetPosition().X - ((mViewportSize.Width / 2.0f) / 16.0f);
    float vct = mCamera->GetTarget()->GetPosition().Y - ((mViewportSize.Height / 2.0f) / 16.0f);

    out.X = vcl + (float(target.X) / 16.0f);
    out.Y = vct + (float(target.Y) / 16.0f);

    return out;
}

void CClient::InputMousePosition(int x, int y) {
    mMousePos.X = x;
    mMousePos.Y = y;
    if (mUiInited) {
        mUiSystem->injectMousePosition(float(x), float(y));
    }
}
void CClient::InputMouseButtonDown(ui::MouseButton b) {
    if (mUiInited) {
        bool processed = mUiSystem->injectMouseButtonDown(b);
        // Check if CEGUI consumed the event
        if (processed) {
            mUiFocused = true;
        }
        else {
            ui::Window* root = mUiSystem->getGUISheet();
            ui::Window* active = root->getActiveChild();
            ui::Window* prev = NULL;
            // Get the active window that is not the GUI sheet
            while (active && active != root) {
                prev = active;
                active = active->getParent();
            }
            active = prev;
            // If the active window wasn't hit, activate root
            if (active && !active->isMousePassThroughEnabled()) {
                if (!active->isHit(ui::Vector2(float(mMousePos.X),
                                               float(mMousePos.Y)))) {
                    active->deactivate();
                    root->activate();
                    mUiFocused = false;
                }
            }
        }
    }
}
void CClient::InputMouseButtonUp(ui::MouseButton b) {
    if (mUiInited) {
        mUiSystem->injectMouseButtonUp(b);
    }
}
void CClient::InputKeyDown(ui::Key::Scan keycode) {
    mKeyPressTable[uint8(keycode)] = true;
    if (mUiInited) {
        mUiSystem->injectKeyDown(keycode);
        //UI::Window* w = UiSystem->getGUISheet()->getActiveChild();
        //if (w != NULL) {
        //    std::cout << "ACTIVE CHILD:" << w->getName().c_str() << std::endl;
        //}
    }
}

void CClient::InputKeyUp(ui::Key::Scan keycode) {
    mKeyPressTable[uint8(keycode)] = false;
    if (mUiInited) {
        mUiSystem->injectKeyUp(keycode);
    }
}

bool CClient::OnTestButtonClicked(const ui::EventArgs& e) {
    ui::Window* w = mUiWindowManager->getWindow("testButton");
    static int clicks = 0;
    clicks++;
    w->setText((boost::format("Clicks: %1%") % clicks).str());
    return true;
}
bool CClient::OnKeyDown(const ui::EventArgs& e) {
    std::cout << "ROOT KEY DOWN" << std::endl;
    return true;
}
void CClient::OnRenderViewDraw(const FRenderView& view, const ui::RenderingContext& context) {
    //if (view.GetCamera()) {
    //    ui::Rect& pr = view.getOuterRectClipper();
    //    FRectF vp(pr.d_left, pr.d_top, pr.d_right - pr.d_left, pr.d_bottom - pr.d_top);
    //    RnDrawScene(view.GetCamera(), vp);
    //}
}

void CClient::SetWindowHandle(uint32 handle) {
    mWindowHandle = handle;
    if (mWindowHandle == 0) {
        mLogger->Debug("Set engine window handle to NULL");
    }
}

void CClient::SetViewportSize(const FSizeI& size) {
    mViewportSize = size;
    mLogger->Info(format("requested resize to: %1%, %2%") % size.Width % size.Height);
    ui::OpenGLRenderer* ren;
    if (mUiInited) {
        ren = static_cast<ui::OpenGLRenderer*>(mUiRenderer);
        ren->grabTextures();
    }
    RnConfig();
    if (ren) {
        ren->restoreTextures();
        ren->setDisplaySize(mViewportSize);
    }
    mLogger->Debug("OpenGL parameters have been reset");
}

//void CEngine::SetMouseVisible(bool osstate, bool uistate){
//    if (osstate == true)
//         stuff
//    else 
//         stuff
//    if (uistate == true)
//        UI::MouseCursor::getSingleton().show();
//    else
//        UI::MouseCursor::getSingleton().hide();
//}

//---------------------------
// CClient UI implementation
//---------------------------

void CClient::UiInit() {
    if (mUiInited)
        return;
    mLogger->Debug("initializing CEGUI");
    mUiRenderer = &ui::OpenGLRenderer::create(ui::Size(float(mViewportSize.Width),
                                                       float(mViewportSize.Height)));
    mUiResourceProvider = new FResourceProvider(mLogger);
    mUiSystem = &ui::System::create(*mUiRenderer, mUiResourceProvider);
    //UI::MouseCursor::getSingleton().hide();
    // Get the default resource provider and set directories
    //UiResourceProvider = static_cast<UI::DefaultResourceProvider*>(UiSystem->getResourceProvider());
    mUiResourceProvider->setResourceGroupDirectory("schemes", "data/gui/schemes/");
    mUiResourceProvider->setResourceGroupDirectory("imagesets", "data/gui/imagesets/");
    mUiResourceProvider->setResourceGroupDirectory("fonts", "data/gui/fonts/");
    mUiResourceProvider->setResourceGroupDirectory("layouts", "data/gui/layouts/");
    mUiResourceProvider->setResourceGroupDirectory("looknfeels", "data/gui/looknfeel/");
    mUiResourceProvider->setResourceGroupDirectory("scripts", "data/scripts/");
    mUiResourceProvider->setResourceGroupDirectory("tilesets", "data/images/tiles/");
    // Specify resource groups for CEGUI subsystems
    ui::Imageset::setDefaultResourceGroup("imagesets");
    ui::Font::setDefaultResourceGroup("fonts");
    ui::Scheme::setDefaultResourceGroup("schemes");
    ui::WidgetLookManager::setDefaultResourceGroup("looknfeels");
    ui::WindowManager::setDefaultResourceGroup("layouts");
    ui::ScriptModule::setDefaultResourceGroup("python_scripts");
    // Get the various managers and load schemes and set defaults
    mUiWindowManager = ui::WindowManager::getSingletonPtr();
    mUiSchemeManager = ui::SchemeManager::getSingletonPtr();
    mUiFontManager = ui::FontManager::getSingletonPtr();
    mUiSchemeManager->create("TaharezLook.scheme");
    mUiSchemeManager->create("VanillaSkin.scheme");
    mUiSchemeManager->create("NeoRice.scheme");
    if (!mUiFontManager->isDefined("DejaVuSans-10"))
        mUiFontManager->create("DejaVuSans-10.font");
    //if (!mUiFontManager->isDefined("Commonwealth-10"))
    //    mUiFontManager->create("Commonwealth-10.font");
    //if (!UiFontManager->isFontPresent("Emulator-10"))
    //    UiFontManager->createFont("Emulator.font");
    mUiSystem->setDefaultFont("DejaVuSans-10"); // First loaded font is default, this is unnecessary
    mUiSystem->setDefaultMouseCursor("Neo-Images", "MouseArrow");
    mUiSystem->setDefaultTooltip("TaharezLook/Tooltip");
    ui::WindowFactoryManager::addFactory<ui::TplWindowFactory<FRenderView>>();
    ui::Window* root = mUiWindowManager->createWindow("DefaultWindow", "root");
    // FIX FOR ROOT INPUT: http://www.cegui.org.uk/phpBB2/viewtopic.php?f=10&t=3496
    mUiSystem->setGUISheet(root);
#if ENABLE_SCALING
    ui::Imageset* is = ui::ImagesetManager::getSingleton().getImageset("TaharezLook");
    is->setAutoScalingEnabled(false);
    //is->setNativeResolution(UI::Size(float(RnViewportSize.Width), float(RnViewportSize.Height)));
#endif
#if ENABLE_MAIN_RENDERVIEW
    ui::Window* rv = mUiWindowManager->createWindow("Kharlia/RenderView", "MainRenderView");
    //rv->setPosition(UVector2(UDim(0.0f, 0.0f), UDim(0.0f, 0.0f)));
    rv->setSize(UVector2(UDim(1.0f, 0.0f), UDim(1.0f, 0.0f)));
    rv->setEnabled(true);
    rv->setZOrderingEnabled(false);
    root->addChildWindow(rv);
    mUiRenderViews.push_back(static_cast<FRenderView*>(rv));
#endif
#if ENABLE_TEST_DIALOG
    ui::FrameWindow* fw =
        static_cast<ui::FrameWindow*>(mUiWindowManager->createWindow("TaharezLook/FrameWindow", "testWindow"));
    root->addChildWindow(fw);
    fw->setPosition(ui::UVector2(ui::UDim(0.3f, 0.0f), ui::UDim(0.3f, 0.0f)));
    //fw->setSize(UI::UVector2(UI::UDim(0, 200), UI::UDim(0, 500)));
    fw->setSize(ui::UVector2(ui::UDim(0.0f, 200.0f), ui::UDim(0.0f, 200.0f)));
    fw->setText("Hello World!");

    ui::Window* text = mUiWindowManager->createWindow("TaharezLook/StaticText", "testText");
    fw->addChildWindow(text);
    text->setPosition(ui::UVector2(ui::UDim(0.0f, 15.0f), ui::UDim(0.0f, 40.0f)));
    text->setSize(ui::UVector2(ui::UDim(1.0f, -34.0f), ui::UDim(1.0f, -85.0f)));
    text->setMinSize(ui::UVector2(ui::UDim(0.0f, 0.0f), ui::UDim(0.0f, 32.0f)));
    text->setText("---------------------------------------\n          MOTD: Mido Rocks\n---------------------------------------\n\n\t    There is a lot of work here! Lots and lots of work. Many hours spent pouring over tricky LookNFeelXMLs! And I'm barely half way! Soooo many widgets to fill out and test. Soo many hours ahead of me on the path to something made in CEGUI that people can look upon and not wish they were puking instead of looking upon the wreck that is 99% of available CEGUI interfaces.\n\t    I'm not sure why they didnt include a Nice and Simple UI like what I'm doing here, everything is either TOO lite or TOO bloated. Get it right people!");
    text->setTooltipText("These are the only widgets\nI have done so far. \n All that XML makes me feel yucky! :(");
    text->setProperty( "VertScrollbar", "True");
    text->setProperty( "HorzFormatting", "WordWrapLeftAligned");
    //text->setProperty("TextColours", "tl:FFe6c888 tr:FFe6c888 bl:FFe6c888 br:FFe6c888");
    //text->setProperty("VertFormatting", "VertCentred"); // TopAligned, BottomAligned, VertCentred
    //text->setProperty("HorzFormatting", "HorzCentred"); // LeftAligned, RightAligned, HorzCentred
    text->setProperty("FrameEnabled", "true");

    ui::PushButton* button =
        static_cast<ui::PushButton*>(mUiWindowManager->createWindow("TaharezLook/SystemButton", "testButton"));
    fw->addChildWindow(button);
    button->setPosition(ui::UVector2(ui::UDim(1.0f, -90.0f), ui::UDim(1.0f, -34.0f)));
    button->setSize(ui::UVector2(ui::UDim(0.0f, 80.0f), ui::UDim(0.0f, 24.0f)));
    button->setText("Click me!");
    button->subscribeEvent(
        ui::PushButton::EventClicked, ui::Event::Subscriber(&CEngine::OnTestButtonClicked, this));
#endif
#if ENABLE_CONSOLE
    ui::Window* con = mUiWindowManager->createWindow("TaharezLook/FrameWindow", "Console/Frame");
    con->setText("Console");
    con->setPosition(UVector2(UDim(0, 32), UDim(0, 32)));
    con->setSize(UVector2(UDim(0, 300), UDim(0, 400)));
    
    //UI::Window* ctext = UiWindowManager->createWindow("TaharezLook/MultiLineEditbox", "Console/Frame/Text");
    //ctext->setText("Console text window!\nTest 1\nTest 2\nTest 3");
    //ctext->setEnabled(false);
    //ctext->setArea(URect(UDim(0, 12), UDim(0, 30), UDim(1, -12), UDim(0.9f, -12)));
    
    ui::Window* cinp = mUiWindowManager->createWindow("TaharezLook/Editbox", "Console/Frame/Input");
    cinp->setText("Console input box");
    //cinp->setArea(URect(UDim(0, 12), UDim(0, 30), UDim(1, -12), UDim(0, 50)));
    //cinp->setArea(URect(UDim(0, 12), UDim(0.9f, 12), UDim(1, -12), UDim(1.0f, -12)));
    cinp->setPosition(UVector2(UDim(0, 6), UDim(1.0f, -30)));
    cinp->setSize(UVector2(UDim(1.0f, -12), UDim(0, 24)));
    cinp->setEnabled(true);
    cinp->setVisible(true);

    root->addChildWindow(con);
    //con->addChildWindow(ctext);
    con->addChildWindow(cinp);
#endif
#if ENABLE_FRAME_COUNTER
    ui::Window *debug_text = mUiWindowManager->createWindow("Vanilla/StaticText", "debugText");
    root->addChildWindow(debug_text);
    debug_text->setPosition(UVector2(ui::UDim(0.0125f, 0.0f), UDim(0.0f, 0.0f)));
    debug_text->setSize(UVector2(ui::UDim(1.0f, 0.0f), UDim(0.0f, 32.0f)));
    debug_text->setMinSize(UVector2(ui::UDim(0.0f, 0.0f), UDim(0.0f, 32.0f)));
    debug_text->setText("...");
    debug_text->setProperty("TextColours", "tl:FFFFFFFF tr:FFFFFFFF bl:FFFFFFFF br:FFFFFFFF");
    debug_text->setProperty("VertFormatting", "VertCentred"); // TopAligned, BottomAligned, VertCentred
    debug_text->setProperty("HorzFormatting", "LeftAligned"); // LeftAligned, RightAligned, HorzCentred
    debug_text->setProperty("FrameEnabled", "false");
    debug_text->setProperty("BackgroundEnabled","false");
    mUiDebugText = debug_text;
#endif
    // Temporary actor texture
    mUiResourceProvider->setResourceGroupDirectory("images", "data/images/");
    TempActorTexture = &mUiRenderer->createTexture("actor.png", "images");
    TempActorTextureR = make_shared<FTexture>("actor.png", "ui.textures", TempActorTexture, GetSelfPtr<CClient>());
    mEngine->GetResourceManager()->RegisterResource(TempActorTextureR);

    mUiInited = true;
}
void CClient::UiFini() {
    if (!mUiInited)
        return;
    mLogger->Debug("exiting CEGUI");

    mEngine->GetResourceManager()->UnloadResources("ui.textures");
    assert(TempActorTextureR->GetState() == RS_UNLOADED);
    TempActorTextureR.reset();

    mUiInited = false;

    TempActorTexture = NULL;
    mUiRenderViews.clear();
    mUiDebugText = NULL;

    ui::System::destroy();
    ui::OpenGLRenderer::destroy(*dynamic_cast<CEGUI::OpenGLRenderer*>(mUiRenderer));
    delete mUiResourceProvider;
    mUiSystem = NULL;
    mUiRenderer = NULL;
    mUiResourceProvider = NULL;
    mUiWindowManager = NULL;
    mUiSchemeManager = NULL;
    mUiFontManager = NULL;
    mRnErrorTexture = NULL;
}

//---------------------------
// CClient Renderer implementation
//---------------------------

void CClient::RnInit() {
    if (mRnInited)
        return;
    mLogger->Debug("initializing renderer");

    assert(mWindowHandle);
    mRnDeviceContext = GetDC(reinterpret_cast<HWND>(mWindowHandle));
    if (!mRnDeviceContext)
        throw RuntimeError("failed to retrieve device context");
    mRnRenderingContext = wglCreateContext(mRnDeviceContext);
    if (!mRnRenderingContext)
        throw RuntimeError("failed to create rendering context");
    wglMakeCurrent(mRnDeviceContext, mRnRenderingContext);

    GLenum err = glewInit();
    if (err != GLEW_OK) {
        format msg("GLEW init fail: %1%");
        msg % glewGetErrorString(err);
        throw RuntimeError(msg);
    }
    const char* vstr = reinterpret_cast<const char*>(glGetString(GL_VERSION));
    if (vstr != NULL)
        mLogger->Info(str("found OpenGL version: ") + str(vstr));
    if (!glewIsSupported("GL_VERSION_2_1"))
        throw RuntimeError("OpenGL 2.1 not supported");
    if (!GLEW_ARB_vertex_buffer_object)
        throw RuntimeError("Vertex Buffer Objects not supported");

    RnConfig();

    mRnInited = true;
}

void CClient::RnFini() {
    if (!mRnInited)
        return;
    mLogger->Debug("exiting GL");
    if (mRnRendering)
        mLogger->Warning("Exiting GL while using rendering context");
    if (mEditorRenderInfo) {
        mEditorRenderInfo.reset();
    }
    RnDeleteVertexBuffers();
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(mRnRenderingContext);
    mRnInited = false;
}

void CClient::RnConfig() {
    glViewport(0, 0, mViewportSize.Width, mViewportSize.Height);
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}

//shared_ptr<FTexture> CClient::RnCreateTexture(str name, str group, str filename) {
//    if (!mUiRenderer)
//        return shared_ptr<FTexture>();
//    CEGUI::Texture* itex = &mUiRenderer->createTexture(PyString_AS_STRING(filename.ptr()), "images");
//    assert(itex);
//    shared_ptr<FTexture> tex(new FTexture(name, group, itex));
//    assert(tex->GetIsValid());
//    // ResourceManager->RegisterResource(tex);
//    return tex;
//}

ui::Texture* CClient::GetRnErrorTexture() {
    try {
        if (mRnErrorTexture == NULL) {
            mRnErrorTexture = &mUiRenderer->createTexture();
            uint8 temp_buff[3072];
            uint8* iter = temp_buff;
            bool c = false;
            for (int i = 0; i < 32; i++) {
                c = !c;
                for (int b = 0; b < 32; b++) {
                    c = !c;
                    *iter++ = 254;
                    *iter++ = c? 254: 0;
                    *iter++ = 254;
                }
            }
            mRnErrorTexture->loadFromMemory(temp_buff,
                                            ui::Size(32, 32),
                                            ui::Texture::PF_RGB);
        }
    }
    catch (ui::Exception& e) {
        mLogger->Error(boost::format("Failed to generate error texture: %1%") % e.getMessage());
        //std::cerr << "! ERROR (engine.cpp line " << __LINE__ << " in " << __FUNCTION__ << ") :" << e.getMessage() << std::endl;
    }
    return mRnErrorTexture;
}

KH_BEGIN_GC(CClient)
    KH_GC_SHARED_PTR(thisx->mEngine)
    KH_GC_SHARED_PTR(thisx->mCamera)
KH_END_GC()

void _InitClientClasses() {
    _InitClientBindings();
}

void _ExitClientClasses() {
}

}} // namespace kharlia::engine